#include "PrePrepare.hpp"

using namespace BFT::order::messages;

PrePrepare::PrePrepare(uint32_t _view, uint32_t _seq, boost::shared_ptr<BFT::messages::HistoryDigest> _hist,
		       boost::shared_ptr<RequestBatch> _batch, boost::shared_ptr<BFT::messages::NonDeterminism> _non,
		       boost::shared_ptr<BFT::messages::Digest> _cp, uint32_t _sendingReplica)
	: MacArrayMessage::MacArrayMessage(PRE_PREPARE, computeSize(_view, _seq, _hist, _batch, _non, _cp),
					   _sendingReplica, BFT::Parameters::getOrderCount())
{
	viewNo = _view;
	seqNo = _seq;
	history = _hist;
	reqBatch = _batch;
	nondet = _non;
	cpHash = _cp;
	
	cleanCount = 0;
	
	//getting bytes and offset
	int bytes_size;
	boost::shared_ptr<char> bytes = getBytes(bytes_size);
	
	int offset = getOffset();
	
	//write the view number
	*(uint32_t*)(bytes.get()+offset) = htonl(viewNo);
	offset += VIEW_NUMBER_SIZE;

	//write the sequence number
	*(uint32_t*)(bytes.get()+offset) = htonl(seqNo);
	offset += SEQUENCE_NUMBER_SIZE;

	//write the history
	int temp_bytes_size = BFT::messages::HistoryDigest::size();
	boost::shared_ptr<char> temp_bytes = history.get()->getBytes();
	memcpy( (void *)(bytes.get()+offset) , (const void *)(temp_bytes.get()) , temp_bytes_size);
	offset += temp_bytes_size;	
	
	//write the nondeterminism size
	temp_bytes_size = BFT::messages::NonDeterminism::size();
	*(uint16_t*)(bytes.get()+offset) = htons(temp_bytes_size);
	offset += NON_DETERMINISM_SIZE_SIZE;
	
	//write the nondeterminism
	temp_bytes= nondet.get()->getBytes();
	memcpy( (void *)(bytes.get()+offset) , (const void *)(temp_bytes.get()) , temp_bytes_size);
	offset += temp_bytes_size;	
	
	//write the CP hash
	temp_bytes_size = BFT::messages::Digest::size();
	temp_bytes= cpHash.get()->getBytes();
	memcpy( (void *)(bytes.get()+offset) , (const void *)(temp_bytes.get()) , temp_bytes_size);
	offset += temp_bytes_size;
	
	//write the size of the batch
	temp_bytes=_batch.get()->getBytes(temp_bytes_size);
	*(uint16_t*)(bytes.get()+offset) = htons(temp_bytes_size);
	offset += ENTRY_BATCH_SIZE_SIZE;
	
	//write the number of entries in the batch
	*(uint16_t*)(bytes.get()+offset) = htons(_batch.get()->getEntries().get()->size());
	offset += ENTRY_COUNT_SIZE;
	
	//write the batch	
	memcpy( (void *)(bytes.get()+offset) , (const void *)(temp_bytes.get()) , temp_bytes_size);
	offset += temp_bytes_size;
}

PrePrepare::PrePrepare(boost::shared_ptr<char> _bytes, int _bytes_size)
	: MacArrayMessage::MacArrayMessage(_bytes, _bytes_size)
{
	if (getTag() != PRE_PREPARE)
	{
		fprintf(stderr, "trying to create PrePrepare from wrong bytes. expected tag: %d actual tag: %d",PRE_PREPARE, getTag());
		exit(96);
	}
	
	cleanCount = 0;
	
	int offset = getOffset();
	
	//read the view number
	viewNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += VIEW_NUMBER_SIZE;
	
	//read the sequence number
	seqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;
	
	//read the history
	int temp_size = BFT::messages::HistoryDigest::size();
	boost::shared_ptr<char> temp_bytes ((char*)malloc(temp_size), free);
	memcpy( (void * )temp_bytes.get() , (const void*)(_bytes.get()+offset) , temp_size);
	offset += temp_size;
	history = BFT::messages::HistoryDigest::fromBytes(temp_bytes, temp_size);
	
	//read the nondet size
	temp_size = ntohs(*(uint16_t*)(_bytes.get()+offset));
	offset += NON_DETERMINISM_SIZE_SIZE;
	
	//read the nondeterminism
	temp_bytes.reset( (char*)malloc(temp_size),free);
	memcpy( (void*)(temp_bytes.get()) , (const void *)(_bytes.get()+offset), temp_size );
	offset += temp_size;
	
	nondet.reset(new BFT::messages::NonDeterminism(temp_bytes, temp_size));
	
	//read the CP hash
	temp_size = BFT::messages::Digest::size();
	temp_bytes.reset((char*)malloc(temp_size),free);
	memcpy( (void*)(temp_bytes.get()) , (const void *)(_bytes.get()+offset), temp_size );
	offset += temp_size;	
	
	cpHash = BFT::messages::Digest::fromBytes(temp_bytes, temp_size);
	
	//read the batch size
	temp_size = ntohs(*(uint16_t*)(_bytes.get()+offset));
	offset += ENTRY_BATCH_SIZE_SIZE;
//	fprintf(stderr, "batch size: %d\n",temp_size);
	
	//read the nmber of entries in the batch
	uint16_t entry_count = ntohs(*(uint16_t*)(_bytes.get()+offset));
	offset += ENTRY_COUNT_SIZE;
//	fprintf(stderr, "number of entries: %d\n",entry_count);
	
	//read the batch
	temp_bytes.reset((char*)malloc(temp_size),free);
	memcpy( (void*)(temp_bytes.get()) , (const void *)(_bytes.get()+offset), temp_size );
	offset += temp_size;
	
//	fprintf(stderr, "bytes size %d, authenticationsize %d, offset %d",_bytes_size, getAuthenticationSize(), offset);
	
	reqBatch.reset(new RequestBatch(temp_bytes, temp_size, entry_count));
	
	if ((unsigned int)offset != (_bytes_size - getAuthenticationSize()))
	{
		fprintf(stderr, "trying to build a PrePrepare from an invalid byte input\n");
		exit(97);
	}
}

bool PrePrepare::equals(boost::shared_ptr<PrePrepare> _pp)
{
	return  MacArrayMessage::equals(_pp) &&
		viewNo == _pp.get()->getView() &&
		seqNo == _pp.get()->getSeqNo() &&
		history.get()->equals(_pp.get()->getHistory()) &&
		cpHash.get()->equals(_pp.get()->getCPHash()) &&
		nondet.get()->equals(_pp.get()->getNonDeterminism()) &&
		reqBatch.get()->equals(_pp.get()->getRequestBatch());
}

uint32_t PrePrepare::computeSize(uint32_t _view, uint32_t _seq, boost::shared_ptr<BFT::messages::HistoryDigest> _history,
				 boost::shared_ptr<RequestBatch> _reqBatch, boost::shared_ptr<BFT::messages::NonDeterminism> _nondet,
				 boost::shared_ptr<BFT::messages::Digest> _cp)
{
	uint32_t size = VIEW_NUMBER_SIZE + 
	                SEQUENCE_NUMBER_SIZE + 
			BFT::messages::HistoryDigest::size() +
			NON_DETERMINISM_SIZE_SIZE + 
			BFT::messages::NonDeterminism::size() +
			BFT::messages::Digest::size() +
			ENTRY_BATCH_SIZE_SIZE +
			ENTRY_COUNT_SIZE +
			_reqBatch.get()->getSize();
	return size;	
}

void PrePrepare::printInfo()
{
	fprintf(stderr,"PrePrepare information:\n");
	fprintf(stdout,"viewNo: %d, seqNo: %d, sender: %d\n",viewNo, seqNo, getSender());
	//history.get()->printInfo();
	reqBatch.get()->printInfo();
	//nondet.get()->printInfo();
	//cpHash.get()->printInfo();
}

void PrePrepare::rcCleaned()
{
	boost::unique_lock<boost::recursive_mutex> lock(cond_mutex);
	cleanCount++;
//	fprintf(stderr,"**** preprepare::rccleaned says: cleanCount %d\n",cleanCount);
	cleancount_condition.notify_all();	
}

void PrePrepare::rcMasterCleaned()
{
	boost::unique_lock<boost::recursive_mutex> lock(cond_mutex);
	cleanCount++;
	while(cleanCount < reqBatch.get()->getEntries().get()->size())
	{
//		fprintf(stderr,"Waiting on a master cleaner\n");
//		fprintf(stderr,"**** preprepare::rcMastercleaned says: cleanCount %d, size %d\n"
//			,cleanCount,reqBatch.get()->getEntries().get()->size());
		cleancount_condition.wait(lock);
	}
//	fprintf(stderr,"**** preprepare::rcMastercleaned says: NO LONGER WAITING\n");
}
